package piwan.editor;

import piwan.*;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

import javax.media.opengl.GL;


public class Editor extends MainWindow implements EditorGui.Listener, FrameListener  {
	private static final long serialVersionUID = 1L;
	
	public Editor() {
		super("Piwan - Editor");
		
	}
	
	public Camera getCamera() { return cam; }
	
	public void glInited() {
		loadTheme("data/themes/QtCurve/qtcurve - editor.xml");
		cam = new Camera(this);
		plrend = new PlaneRenderer(this);
		map = new EditMap(this,cam);
		gui = new EditorGui(this,this);
		
		cam.addHandler();
		
		gl.glDepthFunc(GL.GL_LEQUAL);
		cam.setPerpendicular(true);
		newMap();
		//importImage("gfx/mapimage.png");
		gui.selectRegion(null);
		addFrameListener(this);
		
		sthdl = new StateHandler(Mode.values().length);
		sthdl.setHandler(new ModifyHandler(), Mode.MODIFY.ordinal());
		sthdl.setHandler(new NewPolyHandler(), Mode.NEWPOLY.ordinal());
		sthdl.setHandler(new ConnHandler(), Mode.CONN.ordinal());
		addEventHandler(sthdl);
	}
	
	public void newFrame(double delta) {
		cam.loadMatrices();
		plrend.render();
		if( renderPolys )
			map.render();
		((ModeHandler)sthdl.getHandler()).render();
		display.display();
	}
	
	public void setPolyRenderingEnabled(boolean b) {
		renderPolys = b;
	}
	
	// EditorGui handlers
	public void newMap() {
		openMap("new.map");
	}
	public void importImage( String fileName ) { plrend.loadTexture(fileName); }
	public void newRegion() { sthdl.setState(Mode.NEWPOLY.ordinal()); }
	public void saveMap( String fileName ) { map.saveMap(fileName); }
	public void openMap( String fileName ) { map.loadMap(fileName);gui.setMapInfo(map); }
	public void connectionMode() {
		sthdl.setState(Mode.CONN.ordinal());
	}
	
	// Editor Mode handlers
	private enum Mode { MODIFY, NEWPOLY, CONN };
	private enum SelType { NONE, VERTEX, POLY }
	private abstract class ModeHandler extends EventHandler {
		public abstract void render();
	}
	
	private class ModifyHandler extends ModeHandler {
		public boolean onKeyPressed( KeyEvent e ) {
			if( e.getKeyCode() == KeyEvent.VK_DELETE && (selType==SelType.POLY || selType==SelType.VERTEX) ) {
				map.removePoly(selPoly);
				for(EditVertex v: selPoly.getVertices()) {
					v.removePoly(selPoly);
					if(v.getPolys().size()==0)
						map.removeVertex(v);
				}
				ArrayList<EditPoly> adjs = new ArrayList<EditPoly>(selPoly.getAdjacent());
				for(EditPoly p: adjs)
					p.removeAdjacent(selPoly);
				unselect();
				return true;
			}
			return false;
		}
		public boolean onMousePressed( MouseEvent e ) {
			dragging = true;
			select(cam.getWorldMouse());
			return true;
		}
		
		public boolean onMouseReleased(MouseEvent e) {
			dragging = false;
			return true;
		}
		
		public boolean onMouseMotion(MouseEvent e) {
			
			if(dragging && selType==SelType.VERTEX) {
				selVert.setPosition(cam.getWorldMouse());
			}
			return true;
		}
		
		public void select( Vector2 at ) {
			EditVertex v = map.getVertexAt(at);
			EditPoly p = map.getPolyAt(at);
			if( p==null && v!=null )
				p = v.getPolys().get(0);
			
			if( p!=null )
				select(p,v);
			else 
				unselect();
		}
		
		public void select( EditPoly p, EditVertex v ) {
			assert p!=null;
			if(v==null)
				selType = SelType.POLY;
			else
				selType = SelType.VERTEX;
			selVert = v;
			selPoly = p;
			
			gui.selectRegion(p);
		}
		
		public void unselect() {
			selType = SelType.NONE;
			selVert = null;
			selPoly = null;
			gui.selectRegion(null);
		}
		
		public void render() {
			if(selPoly!=null) {
				gl.glColor4d(1.0,1.0,1.0,1.0);
				selPoly.renderLines(gl);
				gl.glBegin(GL.GL_POINTS);
				gl.glColor4d(1.0,1.0,0.0,1.0);
				selPoly.renderPoints(gl);
				if(selVert!=null) {
					gl.glColor4d(1.0,0.0,0.0,1.0);
					selVert.render(gl);
				}
				gl.glEnd();
			}
		}
		
		private boolean		dragging = false;
		private SelType		selType = SelType.NONE;
		private EditVertex	selVert = null;
		private EditPoly	selPoly = null;
	}
	
	public class NewPolyHandler extends ModeHandler {
		public void activated() {
			newPoly = new EditPoly();
		}
		
		public boolean onMousePressed(MouseEvent e) {
			if(e.getButton() == MouseEvent.BUTTON1)
				addVertex();
			else
				removeVertex();
			
			return true;
		}
		
		private void addVertex() {
			EditVertex nextv = map.getVertexAt(cam.getWorldMouse());
			if(nextv==null)
				map.addVertex( nextv = new EditVertex(cam.getWorldMouse()) );
			
			if( newPoly.getVertexCount() >= 3 && nextv == newPoly.getVertices().get(0) ) {
				newPoly.close();
				map.addPoly(newPoly);
				
				newPoly = null;
				sthdl.setState(Mode.MODIFY.ordinal());
			} else {
				nextv.addPoly(newPoly);
				newPoly.addVertex(nextv);
			}
		}
		
		private void removeVertex() {
			if( newPoly.getVertexCount() > 0 ) {
				EditVertex last = newPoly.getVertices().get( newPoly.getVertexCount() - 1);
				newPoly.removeVertex(last);
				last.removePoly(newPoly);	
				if(last.getPolys().size()==0)
					map.removeVertex(last);
			}
			
			if( newPoly.getVertexCount() == 0 ) {
				newPoly = null;
				sthdl.setState(Mode.MODIFY.ordinal());
			}
		}
		
		public void render() {
			if(newPoly!=null) {
				gl.glColor4d(1.0,1.0,1.0,1.0);
				newPoly.renderLines(gl);
				gl.glBegin(GL.GL_POINTS);
				gl.glColor4d(1.0,1.0,0.0,1.0);
				newPoly.renderPoints(gl);
				gl.glEnd();
			}
		}
		
		private EditPoly newPoly = null;
	}
	
	public class ConnHandler extends ModeHandler {
		public void activated() { startPoly = null; } 
		public void render() {
			gl.glColor4d(1.0, 1.0, 1.0, 1.0);
			gl.glBegin(GL.GL_LINES);
			for(EditPoly p: map.getPolygons())
				for(EditPoly q: map.getPolygons()) {
					if( p.getAdjacent().contains(q) ) {
						gl.glVertex3d(p.getCenter().x, 0.0, p.getCenter().y);
						gl.glVertex3d(q.getCenter().x, 0.0, q.getCenter().y);
					}
				}
			gl.glColor4d(1.0, 1.0, 0.0, 1.0);
			if(startPoly!=null) {
				gl.glVertex3d(startPoly.getCenter().x, 0.0, startPoly.getCenter().y);
				gl.glVertex3d(cam.getWorldMouse().x, 0.0, cam.getWorldMouse().y);
			}
			gl.glEnd();
		}
		
		public boolean onMousePressed( MouseEvent e ) {
			if( e.getButton() == MouseEvent.BUTTON1) {
				EditPoly p = map.getPolyAt(cam.getWorldMouse());
				if(p == null) return false;
				if(startPoly == null)
					startPoly = p;
				else {
					if(startPoly.isAdjacentTo(p))
						startPoly.removeAdjacent(p);
					else
						startPoly.addAdjacent(p);
					startPoly = null;
				}
				return true;
			} else {
				if( startPoly != null )
					startPoly = null;
				else
					sthdl.setState(Mode.MODIFY.ordinal());
				return true;
			}
		}
		
		
		private EditPoly startPoly = null;
	}
	
	public static void main( String[] args ) {
		new Editor();
	}
	
	private Camera cam = null;
	private PlaneRenderer plrend = null;
	private EditorGui gui = null;
	private EditMap map = null;
	private StateHandler sthdl = null;
	private boolean	renderPolys = true;
	
}

